/*
Copyright (c) 2007, EffectiveUI, Inc.

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the EffectiveUI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@ignore
*/
package com.effectiveui.frameworks.jest {
	
	import com.effectiveui.frameworks.jest.events.GestureEvent;
	
	import flash.events.MouseEvent;
	import flash.utils.Dictionary;
	
	
	/**
	 * GestureInterpereter is not generally accessed directly, but can be if needed.
	 * GestureInterpereter holds a registery of all gestures described for the 
	 * application. GestureInterpereter is a singleton. Jest (the class) 
	 * waits for and captures user mouse gestures and passes them to 
	 * GestureInterpereter for execution.
	 * 
	 * @see com.effectiveui.frameworks.jest.Jest
	 * 
	 */	
	public class GestureInterpereter {
		
		private static var instance:GestureInterpereter;
		
		private var flatGestureMap:Dictionary;
		private var commandMap:Dictionary;
		
		public static function getInstance():GestureInterpereter {
			if (instance == null) instance = new GestureInterpereter(new Initializer());
			return instance
		}
		
		public function GestureInterpereter(init:Initializer) {
			if (init == null) throw new Error("GestureInterpereter instance should only be accessed through GestureInterpereter.getInstance()");
			
			flatGestureMap = new Dictionary(true);
			commandMap = new Dictionary(true);
		}
		
		/**
		 * 
		 * Given an array of gesture components, executes the corresponding
		 * CairngormEvent (if one is found). Fails silently.
		 * 
		 * @param gestures Array of strings describing a gesture.
		 * @param originalMouseEvent MouseEvent that originated the mouse gesture.
		 * @param endingMouseEvent MouseEvent that ended the mouse gesture.
		 * 
		 */		
		public function executeGesture(gestures:Array, originalMouseEvent:MouseEvent, endingMouseEvent:MouseEvent):void {
			var gestureKey:String = gestures.join('');
			if (flatGestureMap[gestureKey] != null) {
				var gesture:Gesture = Gesture(flatGestureMap[gestureKey]);
				var commandName:String = gesture.commandName;
				
				var event:GestureEvent = new GestureEvent(gesture, originalMouseEvent, endingMouseEvent);
				event.dispatch();
			}
		}
		
		/**
		 * Registers a gesture for later execution.
		 * 
		 * @param gesture 
		 * 
		 */		
		public function registerGesture(gesture:Gesture):void {
			var gestures:Array			= gesture.gestures;
			var commandName:String		= gesture.commandName;
			var gestureKey:String		= gestures.join('');
			if (matchesGesture(gestures)) {
				var gestureString:String = Gestures.describeAsCompass(gestures);
				throw new Error("Gesture: '" + gestureString + "' already defined");
			}
			flatGestureMap[gestureKey]	= gesture;
			commandMap[commandName]		= gesture;
		}
		
		/**
		 * Unregisters a gesture.
		 * 
		 * @param gesture class
		 * 
		 */		
		public function unregisterGesture(gesture:Gesture):void {
			var commandName:String = gesture.commandName;
			var gestureKey:String = gesture.gestures.join('');
			flatGestureMap[gestureKey] = null;
			delete flatGestureMap[gestureKey];
			commandMap[commandName] = null
			delete commandMap[commandName]
		}
		
		/**
		 * Unregisters a gesture given the corresponding command
		 * 
		 * @param commandName
		 * 
		 */		
		public function unregisterCommand(commandName:String):void {
			unregisterGesture(gestureForCommand(commandName));
		}
		
		/**
		 * Given a command name, returns the corresponding gesture class.
		 * 
		 * @param commandName
		 * @return gesture class
		 * 
		 */		
		public function gestureForCommand(commandName:String):Gesture {
			return Gesture(commandMap[commandName]);
		}
		
		/**
		 * Is the command name registered?
		 * 
		 * @param commandName
		 * @return boolean
		 * 
		 */		
		public function commandRegistered(commandName:String):Boolean {
			return (commandMap[commandName] != null);
		}
		
		/**
		 * Return Gesture given an array of gesture components if it is registered.
		 * 
		 * @param gestures Array of gesture components
		 * @return gesture class
		 * 
		 */		
		public function getGesture(gestures:Array):Gesture {
			var gesture:Gesture = Gesture(flatGestureMap[gestures.join('')]);
			return gesture;
		}
		
		/**
		 * Given gesture components, indicates if gesture is registered.
		 * 
		 * @param gestures Array of gesture components
		 * @return is registered?
		 * 
		 */		
		public function matchesGesture(gestures:Array):Boolean {
			return (getGesture(gestures) != null);
		}
		
	}
}
class Initializer {}